package org.jfsm.core;

@SuppressWarnings("unchecked")
final class Transition {

    enum TType {
        D, T, ST, ET, MT, S2T, SET, SMT, SST
    };

    final TType type;
    final int nextStateID;
    final FSMSignalHandler handler;
    final FSMSignalMHandler handlerM;
    final FSMSignalSHandler handlerS;

    Transition() {
        this.nextStateID = -1;
        this.handler = null;
        this.handlerM = null;
        this.handlerS = null;
        type = TType.D;
    }

    Transition(int stateID, int nextStateID, Object handler) {
        this.nextStateID = nextStateID;
        if (handler instanceof FSMSignalHandler) {
            this.handler = (FSMSignalHandler) handler;
            this.handlerM = null;
            this.handlerS = null;
        } else if (handler instanceof FSMSignalMHandler) {
            this.handler = null;
            this.handlerM = (FSMSignalMHandler) handler;
            this.handlerS = null;
        } else {
            this.handler = null;
            this.handlerM = null;
            this.handlerS = (FSMSignalSHandler) handler;
        }
        type = findType(stateID);
    }

    private final TType findType(int stateID) {
        if (stateID == nextStateID) {
            if (handler != null) {
                return TType.ET;
            } else if (handlerM != null) {
                return TType.MT;
            } else if (handlerS != null) {
                return TType.S2T;
            } else {
                return TType.T;
            }
        } else {
            if (handler != null) {
                return TType.SET;
            } else if (handlerM != null) {
                return TType.SMT;
            } else if (handlerS != null) {
                return TType.SST;
            } else {
                return TType.ST;
            }
        }
    }

    final int invoke(InstanceState context, FSMWorker worker) {
        switch (type) {
        case ST:
            context.setStateID(nextStateID);
        case T:
            return -1;

        case SET:
            context.setStateID(nextStateID);
        case ET:
            RetSignal nextE = handler.handle((FSMContext) context);
            if (nextE == null)
                return -1;
            else {
                worker.nextMessage = nextE.getMsgObject();
                return nextE.getEid();
            }

        case SST:
            context.setStateID(nextStateID);
        case S2T:
            RetSignal nextS = handlerS.handle((FSMContext) context, worker.nextMessage,
                    worker.replyCtx);
            if (nextS == null)
                return -1;
            else {
                worker.nextMessage = nextS.getMsgObject();
                return nextS.getEid();
            }

        case SMT:
            context.setStateID(nextStateID);
        case MT:
            RetSignal nextM = handlerM.handle((FSMContext) context, worker.nextMessage);
            if (nextM == null)
                return -1;
            else {
                worker.nextMessage = nextM.getMsgObject();
                return nextM.getEid();
            }

        default:
            worker.logger.invalid(context, worker.nextMessage);
            System.out.println("FSM invalid transition");
            try {
                Thread.sleep(3000);

            } catch (InterruptedException e) {
            }
            System.exit(1);
            return -1;
        }
    }

}
